package tp.pr5.cityLoader;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Scanner;

import tp.pr5.*;
import tp.pr5.cityLoader.cityLoaderExceptions.WrongCityFormatException;
import tp.pr5.items.Bomb;
import tp.pr5.items.CodeCard;
import tp.pr5.items.Fuel;
import tp.pr5.items.Garbage;

/**
 * City loader from a txt file
 *
 */

public class CityLoaderFromTxtFile 
{
	private ArrayList<Place> arrayPlace; 
	private ArrayList<Street> arrayStreet; 
	private int posAntPlace = -1;
	private int posAntStreet = -1;
	private int posAntItems = -1;
	private int numTargetAnt = 0;
	
	/**
	 * Creates the different array lists
	 * 
	 */
	
	public CityLoaderFromTxtFile()
	{
		arrayPlace = new ArrayList<Place>();
		arrayStreet = new ArrayList<Street>();
	}
	
	/**
	 * Load the city
	 * 
	 * @param file The input stream where the city is stored
	 * @return The city
	 * @throws java.io.IOException When there is some format error in the file (WrongCityFormatException) or some errors in IO operations.
	 */
	
	public City loadCity(java.io.InputStream file) throws java.io.IOException
	{
		BufferedReader buffer = new BufferedReader(new InputStreamReader(file));
		Scanner sc = null;
		Street[] streets = null;

		try
		{
			sc = new Scanner(buffer);
			
			//Check if the first line is BeginCity 
			if(!sc.next().equalsIgnoreCase("BeginCity"))
			{
				sc.close();
				throw new WrongCityFormatException("Etiqueta BeginCity no encontrada");
			}
			loadPlaces(sc);
			loadStreets(sc);
			loadItems(sc);
			//Check if the last line is EndCity 
			if(!sc.next().equalsIgnoreCase("EndCity"))
			{
				sc.close();
				throw new WrongCityFormatException("Etiqueta BeginCity no encontrada");
			}
			sc.close();
			
		} catch(WrongCityFormatException e){
			throw new WrongCityFormatException("Formato incorrecto");
		} catch(java.io.IOException e){
			
		}

		//Converts array list so it can be returned in the city as arrays
    	streets = new Street[arrayStreet.size()];
    	streets = arrayStreet.toArray(streets);

		return new City(streets);

	}

	/**
	 * Loads the part of the file that corresponds to the items
	 * 
	 * @param scan the scanned text
	 * @throws java.io.IOException
	 */
	
	private void loadItems(Scanner scan) throws java.io.IOException
	{
		try
		{
			//Check if the next line he reads is BeginCity 
			if(!scan.next().equals("BeginItems"))
			{
				throw new WrongCityFormatException("Etiqueta BeginItems no encontrada");
			}
			
			String marca = scan.next();
			
			do
			{

				switch (marca)
				{
					case "fuel": 
					{
						int itemPos = scan.nextInt();
						String idFuel = scan.next();
						String descriptionFuel = scan.next();
						//Parse the description
						descriptionFuel = this.parseWithSpaces(descriptionFuel, scan);
						int power = scan.nextInt();
						int times = scan.nextInt();
						if(!scan.next().equals("place"))
						{
							throw new WrongCityFormatException("Etiqueta place no encontrada");
						}
						int numPlaceFuel = scan.nextInt();
						if(numPlaceFuel > arrayPlace.size())
						{
							throw new WrongCityFormatException("Formato incorrecto");
						}
						Fuel fuel = new Fuel(idFuel, descriptionFuel, power, times);
						if(itemPos == this.posAntItems + 1)
						{
							arrayPlace.get(numPlaceFuel).addItem(fuel);
						}
						else
						{
							throw new WrongCityFormatException("Formato incorrecto");
						}
						this.posAntItems++;
						break;
					}
					
					case "codecard":
					{
						int itemPos = scan.nextInt();
						String idCodecard = scan.next();
						String descriptionCodecard = scan.next();
						//Parse the description
						descriptionCodecard = this.parseWithSpaces(descriptionCodecard, scan);
						
						String code = scan.next();
						if(!scan.next().equals("place"))
						{
							throw new WrongCityFormatException("Etiqueta place no encontrada");
						}
						int numPlaceCodeCard = scan.nextInt();
						
						CodeCard codeCard = new CodeCard(idCodecard, descriptionCodecard, code);
						if(itemPos == this.posAntItems + 1)
						{
							arrayPlace.get(numPlaceCodeCard).addItem(codeCard);
						}
						else
						{
							throw new WrongCityFormatException("Formato incorrecto");
						}
						this.posAntItems++;
						break;
					}
						
					case "garbage":
					{
						int itemPos = scan.nextInt();
						String idGarbage = scan.next();
						String descriptionGarbage = scan.next();
						//Parse the description
						descriptionGarbage = this.parseWithSpaces(descriptionGarbage, scan);
						
						int recycledMaterial = scan.nextInt();
						if(!scan.next().equals("place"))
						{
							throw new WrongCityFormatException("Etiqueta place no encontrada");
						}
						int numPlaceGarbage = scan.nextInt();
						
						Garbage garbage = new Garbage(idGarbage, descriptionGarbage, recycledMaterial);
						if(itemPos == this.posAntItems + 1)
						{
							arrayPlace.get(numPlaceGarbage).addItem(garbage);
						}
						else
						{
							throw new WrongCityFormatException("Formato incorrecto");
						}
						this.posAntItems++;
						break;
					}
					
					case "bomb":
					{
						int itemPos = scan.nextInt();
						String idBomb = scan.next();
						String descriptionBomb = scan.next();
						//Parse the description
						descriptionBomb = this.parseWithSpaces(descriptionBomb, scan);
						int damage = scan.nextInt();
						int requirement = scan.nextInt();
						String state = scan.next();
						boolean activated = false;
						if(state.equalsIgnoreCase("on"))
						{
							activated = true;
						}
						if(!scan.next().equals("place"))
						{
							throw new WrongCityFormatException("Etiqueta place no encontrada");
						}
						int numPlaceBomb = scan.nextInt();
						if(numPlaceBomb > arrayPlace.size())
						{
							throw new WrongCityFormatException("Formato incorrecto");
						}
						Bomb bomb = new Bomb(idBomb, descriptionBomb, damage, requirement, activated);
						if(itemPos == this.posAntItems + 1)
						{
							arrayPlace.get(numPlaceBomb).addItem(bomb);
						}
						else
						{
							throw new WrongCityFormatException("Formato incorrecto");
						}
						this.posAntItems++;
						break;
					}
				
				}
			}while(!(marca = scan.next()).equals("EndItems"));
		} catch(WrongCityFormatException e){

		} 
		
	}
	
	/**
	 * Loads the part of the file that corresponds to the streets
	 * 
	 * @param scan scanned text
	 * @throws java.io.IOException
	 */
	
	private void loadStreets(Scanner scan) throws java.io.IOException
	{
		try
		{
			//Checks if the next line is BeginStreets
			if(!scan.next().equals("BeginStreets"))
			{
				throw new WrongCityFormatException("Etiqueta BeginStreets no encontrada");
			}
			String marca = scan.next();
			do
			{
				if(!marca.equals("street"))
				{
					throw new WrongCityFormatException("Etiqueta street no encontrada");
				}
				
				int numStreet = scan.nextInt();
				String sourcePlace = scan.next();
				if(!sourcePlace.equals("place"))
				{
					throw new WrongCityFormatException("Etiqueta place no encontrada");
				}
				int numSource = scan.nextInt();
				String dir = scan.next();
				Direction direction =  null;
				//Checks the direction and assigns its direction
				switch (dir)
				{
					case "south": direction = Direction.SOUTH; break;
					case "north": direction = Direction.NORTH; break;
					case "east": direction = Direction.EAST; break;
					case "west": direction = Direction.WEST; break;
					default: direction = Direction.UNKNOWN;
				}
				String targetPlace = scan.next();
				if(!targetPlace.equals("place"))
				{
					throw new WrongCityFormatException("Etiqueta place no encontrada");
				}
				int numTarget = scan.nextInt();
				if(numTarget != this.numTargetAnt + 1)
				{
					throw new WrongCityFormatException("Formato incorrecto");
				}
				String pass = null;
				String a = scan.next();
				boolean isOpen = a.equals("open");
				//If the door is closed, it reads the password
				if(!isOpen) pass = scan.next();
				
				Street street = new Street(arrayPlace.get(numSource), direction, arrayPlace.get(numTarget), isOpen, pass);
				this.numTargetAnt++;
				if(numStreet == this.posAntStreet + 1)
				{
					arrayStreet.add(numStreet,street);
				}
				else
				{
					throw new WrongCityFormatException("Formato incorrecto");
				}
				this.posAntStreet++;
				}
				
				
			 while(!(marca = scan.next()).equals("EndStreets"));
		} catch(WrongCityFormatException e){
			throw new WrongCityFormatException("Formato incorrecto");
		}
			
	}

	/**
	 * Loads the part of the file that corresponds to the places
	 * 
	 * @param scan scanned text
	 * @throws java.io.IOException
	 */
	
	private void loadPlaces(Scanner scan) throws java.io.IOException
	{
		try
		{
			//Checks if the next line is BeginPlaces
			if(!scan.next().equalsIgnoreCase("BeginPlaces"))
			{
				throw new WrongCityFormatException("Etiqueta BeginPlaces no encontrada");
			}
			String marca = scan.next();
			do
			{
				switch(marca)
				{
					case "bunker":
					{
						if(!marca.equals("bunker"))
						{
							throw new WrongCityFormatException("Etiqueta bunker no encontrada");
						}
						int numPlace = scan.nextInt();
						String name = scan.next();
						String description = scan.next();
						//Parse the description 
						description = this.parseWithSpaces(description, scan);
						
						String a = scan.next();
						boolean isSpaceShip = a.equals("spaceShip");
						
						Bunker place = new Bunker(name, isSpaceShip, description);
						if(numPlace == this.posAntPlace + 1)
						{
							arrayPlace.add(numPlace,place);
						}
						else
						{
							throw new WrongCityFormatException("Formato incorrecto");
						}
						this.posAntPlace++;
						break;
					}
					case "place":
					{
						if(!marca.equals("place"))
						{
							throw new WrongCityFormatException("Etiqueta place no encontrada");
						}
						int numPlace = scan.nextInt();
						String name = scan.next();
						String description = scan.next();
						//Parse the description 
						description = this.parseWithSpaces(description, scan);
						
						String a = scan.next();
						boolean isSpaceShip = a.equals("spaceShip");
						
						Place place = new Place(name, isSpaceShip, description);
						if(numPlace == this.posAntPlace + 1)
						{
							arrayPlace.add(numPlace,place);
						}
						else
						{
							throw new WrongCityFormatException("Formato incorrecto");
						}
						this.posAntPlace++;
						break;
					}
					
				}
			} while(!(marca = scan.next()).equals("EndPlaces"));
		}catch(Exception e){
			throw new WrongCityFormatException("Formato incorrecto");
		}
	}

	/**
	 * Converts all "_" into " " 
	 * 
	 * @param line
	 * @return string parsed
	 */
	
	private String convertLine(String line)
	{
		return line.replace("_", " ");
	}
	
	/**
	 * Gives initial place
	 * 
	 * @return the initial place
	 */
	
	public Place getInitialPlace()
	{
		return arrayPlace.get(0);
	}
	
	/**
	 * Parses the city so it can be read with spaces in their descriptions or without
	 * 
	 * @param cad string to get parsed
	 * @param scan scanned text
	 * @return the parsed string
	 */
	private String parseWithSpaces(String cad, Scanner scan)
	{
		String aux = "";
		String aux1 = cad + "_";
		if(cad.startsWith("\""))
		{
			do
			{
				aux = scan.next();
				aux1 += aux + "_";
			}
			while(!aux.contains("\""));	
			return cad = this.convertLine(aux1);
		}
		else
		{
			return cad = this.convertLine(cad);
		}
	}
	
}
